#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <memory>
#include <cerrno>
#include <unordered_map>
#include <sys/types.h>
#include <sys/socket.h>

#include "log.hpp"
#include "nocopy.hpp"
#include "Epoller.hpp"
#include "Socket.hpp"
#include "comm.hpp"

class Connection;
class TcpServer;

uint32_t EVENT_IN = (EPOLLIN);
uint32_t EVENT_OUT = (EPOLLOUT);

static int g_buffer_size = 128;

using func_t = std::function<void(std::shared_ptr<Connection>)>;
using except_func = std::function<void(std::weak_ptr<Connection>)>;

class Connection
{
public:
    Connection(int sock, std::shared_ptr<TcpServer> tcp_server_ptr)
        : _sock(sock), _tcp_server_ptr(tcp_server_ptr)
    {
    }
    ~Connection()
    {
    }

public:
    void SetHandler(func_t recv_cb, func_t send_cb, except_func except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }
    int SockFd()
    {
        return _sock;
    }
    // for debug
    std::string &InBuffer()
    {
        return _inbuffer;
    }
    std::string &OutBuffer()
    {
        return _outbuffer;
    }
    void AppendInBuffer(const std::string &info)
    {
        _inbuffer += info;
    }
    void AppendOutBuffer(const std::string &info)
    {
        _outbuffer += info;
    }

private:
    int _sock;
    std::string _inbuffer;
    std::string _outbuffer;

public:
    func_t _recv_cb;
    func_t _send_cb;
    except_func _except_cb;
    // 回指指针
    std::shared_ptr<TcpServer> _tcp_server_ptr;

    std::string _ip;
    uint16_t _port;
};

class TcpServer : public nocopy
{
    static const int num = 64;

public:
    TcpServer(uint16_t port, func_t OnMessage)
        : _port(port),
          _OnMessage(OnMessage),
          _quit(true),
          _epoller_ptr(new Epoller()),
          _listensock_ptr(new MySocket())
    {
    }
    ~TcpServer()
    {
    }

public:
    void Init()
    {
        _listensock_ptr->Socket();
        SetNonBlockOrDie(_listensock_ptr->Fd());
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();
        log(Info, "create listen socket success:%d", _listensock_ptr->Fd());
        AddConnection(_listensock_ptr->Fd(), EVENT_IN,
                      std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
    }
    void AddConnection(int sock, uint32_t event, func_t recv_cb, func_t send_cb, except_func except_cb,
                       const std::string &ip = "0.0.0.0", uint16_t port = 8080)
    {
        // 1.给sock也创建一个Connection对象并添加，同时listensock和Connection放入_connections
        std::shared_ptr<Connection> new_connection = std::make_shared<Connection>(sock, std::shared_ptr<TcpServer>(this));
        new_connection->SetHandler(recv_cb, send_cb, except_cb);
        new_connection->_ip = ip;
        new_connection->_port = port;
        // 2.添加到unordered_map
        _connections.insert(std::make_pair(sock, new_connection));
        // 3.添加对应事件，并事件加到内核中，fd，event
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, sock, event);
        log(Debug, "Add a new connection success,sockfd is:%d", sock);
    }
    void Accepter(std::shared_ptr<Connection> connection)
    {
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sock = ::accept(connection->SockFd(), (struct sockaddr *)&peer, &len);
            if (sock > 0)
            {
                uint16_t peerport = ntohs(peer.sin_port);
                char ipbuf[128];
                inet_ntop(AF_INET, &peer.sin_addr.s_addr, ipbuf, sizeof(ipbuf));
                log(Debug, "get new client,get info->[%s:%d],sockfd: %d", ipbuf, peerport, sock);
                SetNonBlockOrDie(sock);
                // _listensock只需要设置_recv_cb,而其他sock需要处理读、写、异常
                AddConnection(sock, EVENT_IN,
                              std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                              std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                              std::bind(&TcpServer::Excepter, this, std::placeholders::_1),
                              ipbuf, peerport);
            }
            else
            {
                if (errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                    break;
            }
        }
    }
    // 事件管理器
    void Recver(std::shared_ptr<Connection> connection)
    {
        std::cout << "haha,got you !!!,sockfd: " << connection->SockFd() << std::endl;
        int sock = connection->SockFd();
        while (true)
        {
            char buffer[g_buffer_size];
            memset(buffer, 0, sizeof(buffer));
            ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0); // 非阻塞读取
            if (n > 0)
            {
                connection->AppendInBuffer(buffer);
            }
            else if (n == 0)
            {
                log(Info, "sockfd: %d,client info: %s:%d quit...", sock,
                    connection->_ip.c_str(), connection->_port);
                connection->_except_cb(connection);
            }
            else
            {
                if (errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    log(Warning, "sockfd: %d,client info: %s:%d recv error...", sock,
                        connection->_ip.c_str(), connection->_port);
                    connection->_except_cb(connection);
                    return;
                }
            }
        }
        // 数据有了，但是不一定全,1.检测，2.如果有完整报文就处理
        _OnMessage(connection);
    }
    void Sender(std::shared_ptr<Connection> connection)
    {
        auto &outbuffer = connection->OutBuffer();
        while (true)
        {
            ssize_t n = send(connection->SockFd(), outbuffer.c_str(), outbuffer.size(), 0);
            if (n > 0)
            {
                // 发送成功
                outbuffer.erase(0, n);
                if (outbuffer.empty())
                    break;
            }
            else if (n == 0)
            {
                // 什么都没发
                return;
            }
            else
            {
                // 发送出错
                if (errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    log(Warning, "sockfd: %d,client info: %s:%d send error...", connection->SockFd(),
                        connection->_ip.c_str(), connection->_port);
                    connection->_except_cb(connection);
                    return;
                }
            }
        }
        if (!outbuffer.empty())
        {
            // 缓冲区未发完，开始对写事件的关心
            EnableEvent(connection->SockFd(), true, true);
        }
        else
        {
            // 缓冲区发完了，关闭对写事件的关心
            EnableEvent(connection->SockFd(), true, false);
        }
    }
    void Excepter(std::weak_ptr<Connection> connection)
    {
        if(connection.expired()) return;
        auto conn = connection.lock();
        // 异常处理
        log(Warning, "Excepter handler sockfd: %d,client info %s:%d excepter handler",
            conn->SockFd(), conn->_ip.c_str(), conn->_port);
        //1.移除对fd的关心
        //EnableEvent(connection->SockFd(),false,false);
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL,conn->SockFd(),0);
        //2.关闭异常的fd
        log(Debug,"close fd: %d done...",conn->SockFd());
        close(conn->SockFd());
        //3.从unorder_map里移除
        log(Debug,"remove fd: %d from _connections...",conn->SockFd());
        auto iter = _connections.find(conn->SockFd());
        if(iter == _connections.end()) return;
        _connections.erase(iter);
        // _connections[conn->SockFd()].reset();
        // _connections.erase(conn->SockFd());
    }
    void EnableEvent(int sock, bool readable, bool writeable)
    {
        uint32_t events = 0;
        events |= (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0 | EPOLLET);
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_MOD, sock, events);
    }
    bool IsConnectionSafe(int fd)
    {
        auto iter = _connections.find(fd);
        if (iter == _connections.end())
            return false;
        else
            return true;
    }
    void Dispatcher(int timeout)
    {
        int n = _epoller_ptr->EpollerWait(revs, num, timeout);
        for (int i = 0; i < n; i++)
        {
            uint32_t events = revs[i].events;
            int sock = revs[i].data.fd;
            // 把事件异常转换为读写问题，
            if ((events & EPOLLERR))
                events |= (EPOLLIN | EPOLLOUT);
            if (events & EPOLLHUP)
                events |= (EPOLLIN | EPOLLOUT);
            // 只处理读写即可
            if ((events & EPOLLIN) && IsConnectionSafe(sock))
            {
                if (_connections[sock]->_recv_cb)
                {
                    _connections[sock]->_recv_cb(_connections[sock]);
                }
            }
            if ((events & EPOLLOUT) && IsConnectionSafe(sock))
            {
                if (_connections[sock]->_send_cb)
                {
                    _connections[sock]->_send_cb(_connections[sock]);
                }
            }
        }
    }
    void Loop()
    {
        _quit = false;
        // AddConnection();
        while (!_quit)
        {
            Dispatcher(3000);
            PrintConnection();
        }
        _quit = true;
    }
    void PrintConnection()
    {
        std::cout << "connections fd list :";
        for (auto &connection : _connections)
        {
            std::cout << connection.second->SockFd() << ", ";
            std::cout << "inbuffer: " << connection.second->InBuffer();
        }
        std::cout << std::endl;
    }

private:
    std::shared_ptr<Epoller> _epoller_ptr;     // 内核
    std::shared_ptr<MySocket> _listensock_ptr; // 监听socket
    std::unordered_map<int, std::shared_ptr<Connection>> _connections;
    struct epoll_event revs[num];
    uint16_t _port;
    bool _quit;
    // 让上层处理信息
    func_t _OnMessage;
};